package de.fhaugsburg.games.boardgames.connect4;

import java.util.TreeSet;

import de.fhaugsburg.games.boardgames.GridPoint;
import de.fhaugsburg.games.boardgames.PlayerMode;
import de.fhaugsburg.games.boardgames.exceptions.GameException;

/**
 * This class contains a method to put a piece in a specific column, also the
 * engine has a method to check if there are 4 connected pieces in one direction
 * and a one to change the current piece for the next player. In addition the
 * class uses an enum for the possible directions in Connect4.
 * 
 * @author r4smus
 */
public class Connect4Engine {
	private Connect4Board board;
	private PlayerMode mode;
	private BinaryPiece currentPiece;
	private int currentRow;
	private int currentColumn;
	private TreeSet<Connect4Directions> directionColl = new TreeSet<Connect4Directions>();

	/**
	 * Constructs a Connect4Board and fills the directonColl attribute with the
	 * possible Connect4Directions.
	 * 
	 * @param board
	 *            (Connect4Board)
	 * @param mode
	 *            (PlayerMode)
	 */
	public Connect4Engine(Connect4Board board, PlayerMode mode) {
		this.board = board;
		this.mode = mode;
		this.currentPiece = BinaryPiece.X;
		setDirectionCollection();
	}

	/**
	 * puts a piece in a specific column, the first column is 0. if the player
	 * has won the game, a message will be printed to the console and
	 * System.exit(0) are called. if the playermode is multi the currentPiece
	 * will be changed.
	 * 
	 * @param column
	 * @throws GameException 
	 */
	public void putPiece(int column) throws GameException {
		currentColumn = column;
		currentRow = board.putPiece(currentPiece, column);
		
	}

	/**
	 * resets the equalPieces attribut in all Connect4Directions to 0.
	 */
	private void resetDirectionColl() {
		for (Connect4Directions element : directionColl) {
			element.resetEqualPieces();
		}
	}

	/**
	 * changes the currentPiece for the next player.
	 */
	public void changePiece() {
		if (currentPiece.equals(BinaryPiece.X))
			currentPiece = BinaryPiece.O;
		else
			currentPiece = BinaryPiece.X;
	}

	/**
	 * fills the directionColl attribut with the possible directions for
	 * Connect4.
	 */
	private void setDirectionCollection() {

		directionColl.add(Connect4Directions.NORTH_EAST);
		directionColl.add(Connect4Directions.SOUTH_WEST);
		directionColl.add(Connect4Directions.EAST);
		directionColl.add(Connect4Directions.SOUTH);
		directionColl.add(Connect4Directions.WEST);
		directionColl.add(Connect4Directions.SOUTH_EAST);
		directionColl.add(Connect4Directions.NORTH_WEST);
	}

	/**
	 * checks if one player hast 4 connected pieces in one direction.
	 * @throws GameException 
	 */
	public boolean gameOver() throws GameException {

		resetDirectionColl();

		for (Connect4Directions direction : directionColl) {

			int tempRow = currentRow + direction.getRowDirection();
			int tempColumn = currentColumn + direction.getColumnDirection();

			while (board.isOnTheBoard(new GridPoint(tempRow,tempColumn)) && !(board.isEmpty(new GridPoint(tempRow,tempColumn))) && board.getPiece(new GridPoint(tempRow,tempColumn)).equals(currentPiece)) {
				direction.incrementEqualPieces();
				tempRow += direction.getRowDirection();
				tempColumn += direction.getColumnDirection();

				if (direction.getEqualPieces() == 3)
					return true;
			}
		}
		int northWest = 0;
		int west = 0;
		int southWest = 0;
		int south = 0;
		int southEast = 0;
		int east = 0;
		int northEast = 0;
		for (Connect4Directions element : directionColl) {

			switch (element) {
			case NORTH_WEST:
				northWest = element.getEqualPieces();
				break;
			case WEST:
				west = element.getEqualPieces();
				break;
			case SOUTH_WEST:
				southWest = element.getEqualPieces();
				break;
			case SOUTH:
				south = element.getEqualPieces();
				break;
			case SOUTH_EAST:
				southEast = element.getEqualPieces();
				break;
			case EAST:
				east = element.getEqualPieces();
				break;
			case NORTH_EAST:
				northEast = element.getEqualPieces();
				break;
			default:
				break;
			}
		}
		if (northWest + southEast == 3)
			return true;
		else if (west + east == 3)
			return true;
		else if (southWest + northEast == 3)
			return true;

		return false;

	}

	public int getCurrentRow() {
		return currentRow;
	}

	public void setCurrentRow(int currentRow) {
		this.currentRow = currentRow;
	}

	public int getCurrentColumn() {
		return currentColumn;
	}

	public void setCurrentColumn(int currentColumn) {
		this.currentColumn = currentColumn;
	}

	/**
	 * sets the playerMode due to a permitted String.
	 * 
	 * @param s
	 */
	public void setPlayerMode(String s) {
		if (s.equals("SP"))
			mode = PlayerMode.SINGLE;
		if (s.equals("MP"))
			mode = PlayerMode.MULTI;
	}

	/**
	 * A enum for the possible directions in Connect4.
	 * 
	 * @author r4smus
	 */
	public enum Connect4Directions {
		NORTH_WEST(-1, -1), WEST(0, -1), SOUTH_WEST(1, -1), SOUTH(1, 0), SOUTH_EAST(1, 1), EAST(0, 1), NORTH_EAST(-1, 1);

		int rowDirection;
		int columnDirection;
		int equalPieces = 0;

		Connect4Directions(int rowDirection, int columnDirection) {
			this.rowDirection = rowDirection;
			this.columnDirection = columnDirection;
		}

		public int getRowDirection() {
			return rowDirection;
		}

		public int getColumnDirection() {
			return columnDirection;
		}

		/**
		 * increment the equalPieces attribute
		 */
		public void incrementEqualPieces() {
			this.equalPieces++;
		}

		/**
		 * 
		 * @return the number of equal Pieces in this direction.
		 */
		public int getEqualPieces() {
			return equalPieces;
		}

		/**
		 * resets the attribute equalPieces to 0.
		 */
		public void resetEqualPieces() {
			this.equalPieces = 0;
		}
	}

	/**
	 * @return return the actual board from the engine.
	 */
	public Connect4Board getConnect4Board() {
		return board;
	}

	/**
	 * @return the acual Piece from the engine.
	 */
	public BinaryPiece getCurrentPiece() {
		return currentPiece;
	}
	public TreeSet<Connect4Directions> getdirectionColl(){
		return directionColl;
	}
	public PlayerMode getPlayerMode(){
		return mode;
	}

}
